﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ProgrammingLinearSimplexMethod
{
    public partial class Form1 : Form
    {
        public ModelRunner runner;
        public List<ModelRunner> stepsOptim;
        public Form1()
        {
            InitializeComponent();
            runner = new ModelRunner();
            stepsOptim = new List<ModelRunner>();
            cboTypeOpt.SelectedItem = "MAX";
        }

        private void btnCanonicalForm_Click(object sender, EventArgs e)
        {
            //get data from standard form
            int numArgs = getDatafromStandForm();

            //get data from canonical form
            getDataFromCanonicalForm(numArgs);

            //set canonical form
            setCanonicalFormAndFillTable();
        }

        private void btnOptimalize_Click(object sender, EventArgs e)
        {
            stepsOptim.Clear();

            //get data from standard form
            int numArgs = getDatafromStandForm();

            //get data from canonical form
            getDataFromCanonicalForm(numArgs);

            //set canonical form
            setCanonicalFormAndFillTable();

            runner.run();

            showOptimalizeResults();

            updateRecorder();
        }

        private int getDatafromStandForm()
        {
            int amountFactors = new int();
            List<int> negativeFactors = standardForm(ref amountFactors, txtStandardForm.Text);
            string[] elem = txtStandardForm.Text.Split(new char[] { '+', 'x', '-' }, StringSplitOptions.RemoveEmptyEntries);
            uint iter = 0;
            runner.factorStandForm = new double[elem.Length / 2];
            for (uint i = 0; i < elem.Length; i += 2)
            {
                runner.factorStandForm[iter++] = double.Parse(elem[i]);
                if(negativeFactors.Count != 0){
                    foreach (int negativeIndex in negativeFactors.ToArray()){
                        if (i == (negativeIndex - 1) * 2) runner.factorStandForm[iter - 1] = -runner.factorStandForm[iter - 1];  
                    }
                }
            }
            return elem.Length;
        }

        private List<int> standardForm(ref int amountFactors, string stringTMP)
        {
            amountFactors = 1;     //where is factor with first negative factor
            List<int> minusIndex = new List<int>();
            List<char> standForm = stringTMP.Replace(" ", "").ToCharArray().ToList<char>();
            if (standForm[0] != '-')
            {
                standForm.Insert(0, '+');
            }
            for (int i = 0; i < standForm.Count; i++)
            {
                if (standForm[i] == '-')
                {
                    minusIndex.Add(amountFactors);
                    amountFactors++;
                }
                else if (standForm[i] == '+')
                {
                    amountFactors++;
                }
            }
            return minusIndex;
        }

        private void getDataFromCanonicalForm(int numArgs)
        {
            string[] aLines = txtLimit.Text.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            runner.factorConditions = new double[aLines.Length, numArgs / 2];
            runner.intercepts = new double[aLines.Length];
            for (uint i = 0; i < aLines.Length; i++)
            {
                string[] argsAndInterc = aLines[i].Split(new string[] { "<=", ">=", "<", ">", "=" }, StringSplitOptions.RemoveEmptyEntries);
                int amountFactors = new int();
                List<int> negativeFactors = standardForm(ref amountFactors, argsAndInterc[0]);
                string[] aCondArgs = argsAndInterc[0].Split(new char[] { '+', '-', 'x' }, StringSplitOptions.RemoveEmptyEntries);
                uint iter = 0;
                for (int j = 0; j < numArgs; j += 2)
                {
                    runner.factorConditions[i, iter++] = double.Parse(aCondArgs[j]);
                    if (negativeFactors.Count != 0)
                    {
                        foreach (int negativeIndex in negativeFactors.ToArray())
                        {
                            if (j == (negativeIndex - 1) * 2) runner.factorConditions[i, iter - 1] = -runner.factorConditions[i, iter - 1];
                        }
                    }
                }
                runner.intercepts[i] = Double.Parse(argsAndInterc[1]);
            }
        }

        private void setCanonicalFormAndFillTable()
        {
            runner.factorsCanonForm = new double[runner.factorStandForm.Length + runner.factorConditions.GetLength(0)];
            runner.helpfulPointer = new double[runner.factorStandForm.Length + runner.factorConditions.GetLength(0)];
            runner.optimalPointer = new double[runner.factorStandForm.Length + runner.factorConditions.GetLength(0)];
            runner.exitCriterion = new double[runner.factorConditions.GetLength(0)];
            runner.factorCurrentSolutionValues = new double[runner.factorConditions.GetLength(0)];
            runner.factorCurrentSolutionIndexes = new double[runner.factorConditions.GetLength(0)];

            txtCanonForm.Text = String.Empty;
            uint iter = 1;
            for (uint i = 0; i < runner.factorStandForm.Length; i++)
            {
                txtCanonForm.Text += runner.factorStandForm[i].ToString() + "*x" + iter++ + " + ";
                runner.factorsCanonForm[i] = runner.factorStandForm[i];
            }
            for (uint i = 0; i < runner.factorConditions.GetLength(0) - 1; i++) txtCanonForm.Text += "0*x" + iter++ + " + ";
            txtCanonForm.Text += "0*x" + iter + "  --> " + cboTypeOpt.SelectedItem.ToString() + Environment.NewLine + Environment.NewLine;

            //also fill main table for simplex method
            runner.mainTableSimplex = new double[runner.intercepts.Length, runner.factorStandForm.Length + runner.factorConditions.GetLength(0)];
            //iter = (uint)runner.factorConditions.GetLength(0);
            iter = (uint)runner.factorStandForm.Length;
            for (uint i = 0; i < runner.factorConditions.GetLength(0); i++)
            {
                for (uint j = 0; j < runner.factorStandForm.Length; j++)
                {
                    txtCanonForm.Text += runner.factorConditions[i, j].ToString() + "*x" + (j + 1).ToString() + " + ";
                    runner.mainTableSimplex[i, j] = runner.factorConditions[i, j];
                }
                txtCanonForm.Text += "1*x" + (iter + 1).ToString() + " = " + runner.intercepts[i] + Environment.NewLine;

                runner.factorCurrentSolutionIndexes[i] = iter + 1;
                runner.mainTableSimplex[i, iter++] = 1;
            }
        }

        private void showOptimalizeResults()
        {
            txtOptimResult.Text = String.Empty;
            txtOptimResult.Text = "Successfully optimization to " + cboTypeOpt.SelectedItem + Environment.NewLine + Environment.NewLine +
                "Value of aim fnction: " + runner.currentSolution + Environment.NewLine + Environment.NewLine +
                "Value of canonical function arguments" + Environment.NewLine;
            for (uint i = 0; i < runner.factorsCanonForm.Length; i++)
            {
                bool isSetted = false;
                for (uint j = 0; j < runner.factorCurrentSolutionIndexes.Length; j++)
                    if ((runner.factorCurrentSolutionIndexes[j] - 1) == i)
                    {
                        txtOptimResult.Text += "x" + runner.factorCurrentSolutionIndexes[j] + " = " + runner.intercepts[j] + Environment.NewLine;
                        isSetted = true;
                        break;
                    }

                if (!isSetted) txtOptimResult.Text += "x" + (i + 1).ToString() + " = 0" + Environment.NewLine;
            }
        }

        private void updateRecorder()
        {
            cboStepNo.Items.Clear();
            for (uint i = 0; i < runner.stepsList.Count; i++) cboStepNo.Items.Add(i.ToString());
            cboStepNo.SelectedItem = "0";

            //Main and optimal tables
            for (uint i = 1; i <= runner.factorsCanonForm.Length; i++)
            {
                grdTable.Columns.Add("colMain" + i, "x" + i);
                grdTableOptim.Columns.Add("colOptim" + i, "x" + i);
            }
            grdTable.Rows.Add(runner.mainTableSimplex.GetLength(0));
            grdTableOptim.Rows.Add(1);

            //Current fraction and cond value tables
            grdCurrFactFun.Columns.Add("colFrac1", "");
            grdCurrFactFun.Columns.Add("colFrac2", "");
            grdCurrFactFun.Columns[0].Width = 50;
            grdCurrFactFun.Columns[1].Width = 50;
            grdCurrFactFun.Rows.Add(runner.factorCurrentSolutionValues.Length);

            //Conditional values tables
            grdCondBound.Columns.Add("colCond", "");
            grdCondBound.Columns[0].Width = 50; 
            grdCondBound.Rows.Add(runner.factorCurrentSolutionValues.Length);

            displayTables();
        }

        private void displayTables()
        {
            //Main table
            for (int i = 0; i < runner.mainTableSimplex.GetLength(0); i++)
                for (int j = 0; j < runner.mainTableSimplex.GetLength(1); j++)
                {
                    grdTable.Rows[i].Cells[j].Value = runner.stepsList[cboStepNo.SelectedIndex].mainTableSimplex[i, j];
                    grdTableOptim.Rows[0].Cells[j].Value = runner.stepsList[cboStepNo.SelectedIndex].optimalPointer[j];
                }
                
            //result
            txtAimFunction.Text = runner.stepsList[cboStepNo.SelectedIndex].currentSolution.ToString();

            //Current fraction and cond value tables
            for (int i = 0; i < runner.factorCurrentSolutionValues.Length; i++)
            {
                grdCurrFactFun.Rows[i].Cells[0].Value = runner.stepsList[cboStepNo.SelectedIndex].factorCurrentSolutionValues[i];
                grdCurrFactFun.Rows[i].Cells[1].Value = "x" + runner.stepsList[cboStepNo.SelectedIndex].factorCurrentSolutionIndexes[i].ToString();
                grdCondBound.Rows[i].Cells[0].Value = runner.stepsList[cboStepNo.SelectedIndex].intercepts[i];
            }
        }

        private void txtStandardForm_MouseHover(object sender, EventArgs e)
        {
            lblHelp.Text = "Example: 2x1 + 3x2 + 1x3";
        }

        private void txtStandardForm_MouseLeave(object sender, EventArgs e)
        {
            lblHelp.Text = "Mouse hover element to help";
        }

        private void txtLimit_MouseHover(object sender, EventArgs e)
        {
            lblHelp.Text = "Example: 4x1 + 0x2 + 3x3 <= 9";
        }

        private void txtLimit_MouseLeave(object sender, EventArgs e)
        {
            lblHelp.Text = "Mouse hover element to help";
        }

        private void cboTypeOpt_SelectionChangeCommitted(object sender, EventArgs e)
        {
            runner.typeOptim = cboTypeOpt.SelectedItem.ToString();
        }

        private void cboStepNo_SelectionChangeCommitted(object sender, EventArgs e)
        {
            displayTables();
        }
    }

    public class ModelRunner
    {
        public ModelRunner()
        {
            this.typeOptim = "MAX";
            this.exitIndex = 0;
            this.minIndexExitRow = 0;
            stepsList = new List<StepsMemo>();
        }
        public double[] factorStandForm;   //współczynniki postaci standardowej
        public double[] factorsCanonForm;   //współczynniki postaci kanonicznej
        public double[] helpfulPointer;     //wskaźnik pomocniczy
        public double[] optimalPointer;     //wskaźnik optymalności
        public double[] exitCriterion;  //kryteria wyjścia
        public double[] factorCurrentSolutionValues; //współczynniki funkcji celu bieżącego rozwiązania
        public double[] factorCurrentSolutionIndexes; //indeksy współczynników funkcji celu bieżącego rozwiązania
        public double[] intercepts;    //interceps of conditions
        public double[,] factorConditions;   //condition arguments
        public double[,] mainTableSimplex;  //tabela ze współczynnikami przy zminnych szukanych i swobodnych
        public double currentSolution;  //wartość funkcji celu bieżącego rozwiązania
        public List<StepsMemo> stepsList;
        public string typeOptim;
        private uint exitIndex;
        private uint minIndexExitRow;

        public void run()
        {
            rememberStep();
            while (this.stepOne() == false)
            {
                this.stepTwo();
                this.stepThree();
                this.stepFour();

                rememberStep();
            }
            rememberStep();
        }

        public Boolean stepOne()
        {
            Boolean isOptimal = true;
            //HERE I SHOULD COPY THE TABLE TO REMEMBER VALUES
            for (int i = 0; i < this.factorsCanonForm.Length; i++)
            {
                this.helpfulPointer[i] = 0;
                for (uint n = 0; n < this.factorCurrentSolutionValues.Length; n++) this.helpfulPointer[i] += this.mainTableSimplex[n, i] * this.factorCurrentSolutionValues[n];
                this.optimalPointer[i] = this.factorsCanonForm[i] - this.helpfulPointer[i];
                this.currentSolution = scalarMatrixMultiply(this.factorCurrentSolutionValues, this.intercepts);

                if (this.typeOptim == "MAX" && this.optimalPointer[i] > 0) isOptimal = false;
                else if (this.typeOptim == "MIN" && this.optimalPointer[i] < 0) isOptimal = false;
            }
            return isOptimal;
        }

        public void stepTwo()
        {
            for (uint i = 0; i < this.optimalPointer.Length; i++)
                if (this.typeOptim == "MAX" && this.optimalPointer.Max() == this.optimalPointer[i])
                {
                    this.exitIndex = i;
                    break;
                }
                else if (this.typeOptim == "MIN" && this.optimalPointer.Min() == this.optimalPointer[i])
                {
                    this.exitIndex = i;
                    break;
                }

            for (uint i = 0; i < this.intercepts.Length; i++)
                if (this.mainTableSimplex[i, exitIndex] == 0) this.exitCriterion[i] = 1000000000;
                else this.exitCriterion[i] = this.intercepts[i] / this.mainTableSimplex[i, exitIndex];
        }

        public void stepThree()
        {
            double minValueExit = 10000;
            for (uint i = 0; i < this.exitCriterion.Length; i++)
                if (this.exitCriterion[i] >= 0)
                {       //czy tu dla maksymilizacji trzeba zrobić "else if" i znaleźć max w tabeli wyjścia?
                    minValueExit = this.exitCriterion[i];
                    this.minIndexExitRow = i;
                    break;
                }
            for (uint i = 0; i < this.exitCriterion.Length; i++)
                if (this.exitCriterion[i] >= 0 && this.exitCriterion[i] < minValueExit)
                {
                    minValueExit = this.exitCriterion[i];
                    this.minIndexExitRow = i;

                }

            this.factorCurrentSolutionValues[this.minIndexExitRow] = this.factorsCanonForm[this.exitIndex];
            this.factorCurrentSolutionIndexes[this.minIndexExitRow] = this.exitIndex + 1;
        }

        public void stepFour()
        {
            double divideValue = this.mainTableSimplex[this.minIndexExitRow, this.exitIndex];
            for (uint i = 0; i < this.mainTableSimplex.GetLength(1); i++) this.mainTableSimplex[this.minIndexExitRow, i] /= divideValue;
            this.intercepts[this.minIndexExitRow] /= divideValue;

            for (uint i = 0; i < this.mainTableSimplex.GetLength(0); i++)
            {
                if (i != this.minIndexExitRow)
                {
                    double multiplyValue = this.mainTableSimplex[i, this.exitIndex];
                    for (uint j = 0; j < this.mainTableSimplex.GetLength(1); j++) this.mainTableSimplex[i, j] -= this.mainTableSimplex[this.minIndexExitRow, j] * multiplyValue;

                    this.intercepts[i] -= this.intercepts[this.minIndexExitRow] * multiplyValue;
                }
            }
        }

        private double scalarMatrixMultiply(double[] vector1, double[] vector2)
        {
            double result = 0;
            for (uint i = 0; i < vector1.Length; i++) result += vector1[i] * vector2[i];
            return result;
        }

        private void rememberStep()
        {
            StepsMemo tmpStep = new StepsMemo();
            tmpStep.optimalPointer = new double[this.factorStandForm.Length + factorConditions.GetLength(0)];
            tmpStep.exitCriterion = new double[this.factorConditions.GetLength(0)];
            tmpStep.factorCurrentSolutionValues = new double[this.factorConditions.GetLength(0)];
            tmpStep.factorCurrentSolutionIndexes = new double[this.factorConditions.GetLength(0)];
            tmpStep.intercepts = new double[this.intercepts.Length];
            tmpStep.mainTableSimplex = new double[this.mainTableSimplex.GetLength(0), this.mainTableSimplex.GetLength(1)];
            tmpStep.currentSolution = this.currentSolution;

            for (uint i = 0; i < this.mainTableSimplex.GetLength(0); i++)
                for (uint j = 0; j < this.mainTableSimplex.GetLength(1); j++)
                {
                    tmpStep.mainTableSimplex[i, j] = this.mainTableSimplex[i, j];
                    tmpStep.optimalPointer[j] = this.optimalPointer[j];
                }

            for (uint i = 0; i < this.intercepts.Length; i++)
            {
                tmpStep.exitCriterion[i] = this.exitCriterion[i];
                tmpStep.factorCurrentSolutionValues[i] = this.factorCurrentSolutionValues[i];
                tmpStep.factorCurrentSolutionIndexes[i] = this.factorCurrentSolutionIndexes[i];
                tmpStep.intercepts[i] = this.intercepts[i];
            }

            this.stepsList.Add(tmpStep);
        }
    }

    public struct StepsMemo
    {
        public double[] optimalPointer;     //wskaźnik optymalności
        public double[] exitCriterion;  //kryteria wyjścia
        public double[] factorCurrentSolutionValues; //współczynniki funkcji celu bieżącego rozwiązania
        public double[] factorCurrentSolutionIndexes; //indeksy współczynników funkcji celu bieżącego rozwiązania
        public double[] intercepts;    //interceps of conditions
        public double[,] mainTableSimplex;  //tabela ze współczynnikami przy zminnych szukanych i swobodnych
        public double currentSolution;  //wartość funkcji celu bieżącego rozwiązania
    }
}